﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace TestObjectAsArray
{
    public class ObjectAsArray {
        private Object[] m_Fields;

        public String A {
            get { return (String) m_Fields[ 0 ]; }
            set { m_Fields[ 0 ] = value; }
        }
    }

    public class ObjectAsFuncCollection {
        private Func<Object, object>[] m_Funcs;
        public int MyValue { get; set; }
        
        public Object this[ int index ] {
            get { return m_Funcs[ index ]( this ); }
        }
        
        public ObjectAsFuncCollection() {
            m_Funcs = new Func<object, object>[1];
            m_Funcs[ 0 ] = Emit.WrapGetter<object>( typeof( ObjectAsFuncCollection ).GetProperty( "MyValue" ) );
        }
    }

    public class BindingSource {
        private Func<Object> m_GetterFunc;

        // weakreferences
        private Object m_GetterSource;

        private Object m_CachedValue;

        public Object Get { 
            get { return m_GetterFunc(); }
        }

        public BindingSource(Object source_object, String source_property) {
            Func<Object, Object> core_getter = delegate(object o) { return null; };
            m_GetterFunc = delegate {
                if (m_CachedValue == null){
                    m_CachedValue = core_getter(m_GetterSource);
                }
                return m_CachedValue;
                           };
        }
    }

    public class BindingTarget {
        private Action<Object, Object> m_SetterFunc;
        // weakref
        private Object m_SetterSource;

        public BindingTarget( Object target_object, String target_property ) { }
    }

    public class QuickBinding {
        private Func< Object, Object > m_GetterFunc;
        // weakreferences
        private Object m_GetterSource;
        private Action< Object, Object > m_SetterFunc;
        // weakref
        private Object m_SetterSource;

        // weakref
        private Object m_CachedValue;

        private String SourcePath;
        public QuickBinding( Object source_object, String source_property, Object target_object, String target_property ) {
            INotifyPropertyChanged notify = source_object as INotifyPropertyChanged;
            if( notify != null ) {
               notify.PropertyChanged += HandlePropertyChanged;
            }
        }
        
        private void HandlePropertyChanged( object sender, PropertyChangedEventArgs property_changed_event_args ) {
            if( property_changed_event_args.PropertyName == SourcePath || 
                String.IsNullOrEmpty( property_changed_event_args.PropertyName) ) {
                
                ChangeNotify();
                m_SetterFunc( m_SetterSource, m_GetterFunc( m_GetterSource ) );
            }
        }

        public event Action ChangeNotify;
    }

    public class Emit {

        public static Func< object, T > WrapGetter<T>( PropertyInfo property ) {
            MethodInfo method = property.GetGetMethod();

            var dm = new DynamicMethod( method.Name, typeof(T), new Type[] {
                                                                                      typeof(Object),
                                                                                  }, method.DeclaringType, true );
            var il = dm.GetILGenerator();

            if( !method.IsStatic ) {
                il.Emit( OpCodes.Ldarg_0 );
                il.Emit( OpCodes.Unbox_Any, method.DeclaringType );
            }

            il.EmitCall( method.IsStatic || method.DeclaringType.IsValueType
                             ? OpCodes.Call
                             : OpCodes.Callvirt, method, null );

            if( method.ReturnType.IsValueType ) {
                il.Emit(OpCodes.Box, method.ReturnType);
            }

            il.Emit( OpCodes.Ret );
            Type generic = typeof( Func< , > );

            return ( Func< object, T > ) dm.CreateDelegate(
                typeof( Func< Object, T > ) );
        }
    }
}
